package kr.ac.ssu.ailab.Model.Data.Agent;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
import java.util.Scanner;
import java.util.Vector;

import javax.swing.JPanel;

import kr.ac.ssu.ailab.Control.NewHeuristicPathFinding;
import kr.ac.ssu.ailab.Model.Data.Cell;

/**
 * @author neil
 * @class Agent
 * @brief 각 NPC는 agent 개념의 속성을 갖게 되고, agent 는 group 의 일원이 된다. 각 agent 들은 group 에
 *        전달된 path 를 따라 집단 이동하게 되고, 각 level 과 상황에 따른 reaction 을 취하게 된다.
 */
public class Agent extends JPanel implements Runnable
{
	private Thread agentThread;
	private boolean isAlive;
	private String ID;
	private String type;
	private Integer level;
	private Integer hp;
	private Integer speed;
	private Point currentLocation;
	private Vector<Cell> agentPath;
	private MSG msg;
	private Iterator<Cell> iter;
	private Point temp;

	private int action;

	/**
	 * @constructor
	 * @brief
	 */
	public Agent(String ID, String type, Point point)
	{
		this.agentThread = new Thread(this);
		this.isAlive = true;
		this.ID = ID;
		this.type = type;
		this.hp = 100;
		this.speed = 100;
		this.currentLocation = point;
		this.action = MSG.WAIT;
		this.agentPath = new Vector<Cell>();

		this.setOpaque(false);

		if (this.currentLocation.x < 0)
			this.currentLocation.x = -this.currentLocation.x;
		if (this.currentLocation.y < 0)
			this.currentLocation.y = -this.currentLocation.y;
		this.setBounds(this.currentLocation.x, this.currentLocation.y, 10, 10);

	}

	/**
	 * @brief return individual Thread for taking order from Manager Class
	 * @param null
	 */
	public Thread getAgentThread()
	{
		return this.agentThread;
	}

	/**
	 * @brief return this Agent's state
	 * @param null
	 */
	public int getAction()
	{
		return this.action;
	}

	/**
	 * @brief status about this agent is being alive or dead.
	 * */
	public boolean isAlive()
	{
		return this.isAlive;
	}

	public void setDead()
	{
		this.isAlive = false;
		try
		{
			this.agentThread.join();
			this.agentThread.interrupt();
		}
		catch (InterruptedException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * @brief Set each Agent's Path. If new order arrived new path will be set.
	 * @param null
	 */
	public void setAgentPath(Vector<Cell> path)
	{
		Vector<Cell> tempList = (Vector<Cell>) path.clone();
		this.agentPath = new Vector<Cell>();
		while(!tempList.isEmpty())
		{
			this.agentPath.add(tempList.lastElement());
			tempList.remove(tempList.lastElement());
		}

		System.err.println("path in Agent : "+ this.ID +" : " + this.agentPath);
		this.temp = agentPath.firstElement().getPosition();
		this.iter = agentPath.iterator();
	}

	/**
	 * @brief move each Agent following given path. the arrangement of group should be kept.
	 * @param null
	 */
	private void moveAgent()
	{
		synchronized (NewHeuristicPathFinding.getInstance().getLoop())
		{
			if (this.iter.hasNext())
			{
				int x = temp.x;
				int y = temp.y;

				this.temp = this.iter.next().getPosition();

				this.currentLocation.x += (temp.x - x);
				this.currentLocation.y += (temp.y - y);

				System.out.println(ID + " " + type + " : " + "is Moving. to "
						+ this.currentLocation);
				
				System.out.println(this.ID + " is running at " + this.speed + "speed");

			}
			else
			{
				System.err.println("arrived goal");
				this.action = MSG.WAIT;
			}

			this.setLocation(this.currentLocation);
		}

	}


	/**
	 * @brief set this agent's health according to damage
	 * the speed of each agent will be changed by health.
	 * @param null
	 */
	private void setHP()
	{

		if (this.hp > 0)
			this.speed = 300;
		if (this.hp > 25)
			this.speed = 150;
		if (this.hp > 50)
			this.speed = 100;
		if (this.hp > 75)
			this.speed = 50;
		if (this.hp < 0)
			this.setDead();

		// System.err.println(this.ID + "HP : " + this.hp);
		// System.err.println(this.ID + "Speed : " + this.speed);
	}


	/**
	 * @brief return Agent's ID
	 * @param null
	 */
	public String getID()
	{
		return this.ID;
	}


	/**
	 * @brief set type of this Agent.
	 * @param String aType
	 */
	public void setAgentType(String aType)
	{
		this.type = aType;
	}


	/**
	 * @brief return type of this Agent.
	 * @param null
	 */
	public String getAgentType()
	{
		return this.type;
	}


	/**
	 * @brief check if this agent is waiting for Message.
	 * @param null
	 */
	private boolean waitingForMSG()
	{
//		synchronized (ID)
//		{
//			System.err.println(this.ID + " " + this.type + " is waiting for Msg");
//		}

		return true;
	}

	/**
	 * @brief get a Message from Agent Manager
	 * @param MSG msg 
	 */
	public void receiveMSG(MSG msg)
	{
		this.msg = msg;

		switch (msg.getOrder())
			{
			case MSG.MOVE:
				// System.out.println(this.ID + " gets Msg Move to " +
				// msg.getPath());
				setAgentPath(msg.getPath());
				this.action = MSG.MOVE;
				break;
			case MSG.BEING_ATTACKED:
				this.hp -= msg.getDamage();
				System.out.println(this.ID + " gets damaged by " + msg.getDamage());
				this.setHP();
				break;
			case MSG.HIDE:
			}
	}

	@Override
	public void paint(Graphics graphics)
	{
		// TODO Auto-generated method stub
		super.paint(graphics);

		synchronized (graphics)
		{
			if (this.type.equals("leader"))
				graphics.setColor(new Color(this.hp, 0, 0));
			else
				graphics.setColor(new Color(0, this.hp, 0));

			graphics.fillRect(0, 0, 10, 10);

		}

	}

	@Override
	public void run()
	{
		synchronized (ID)
		{
			// TODO Auto-generated method stub
			while (this.isAlive)
			{
				setHP();
				try
				{
					this.agentThread.sleep(this.speed);
				}
				catch (InterruptedException e)
				{
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				switch (this.action)
					{
					case MSG.WAIT:
						this.waitingForMSG();
						break;
					case MSG.MOVE:
						this.moveAgent();
						this.repaint();
						break;
					}

			}
		}

	}

}
